package schemax

import (
	"gitee.com/zhongguo168a/gocodes/datax"
	"gitee.com/zhongguo168a/gocodes/datax/mapx"
	"gitee.com/zhongguo168a/gocodes/datax/schemax/basickind"
	"gitee.com/zhongguo168a/gocodes/myx/errorx"
	"strconv"
)

// JustifyObject 如果是数字，会转化成正确的类型
func JustifyObject(obj datax.M, decl *ClassDecl) (result datax.M, err error) {
	return justifyObject(obj, decl)
}

// todo: 不完整
func justifyObject(obj datax.M, decl *ClassDecl) (datax.M, error) {
	result := datax.M{}
	for _, field := range decl.GetAllField() {
		fname := field.Name
		ival, has := obj[fname]
		if !has && ival == nil {
			continue
		}
		switch ftyp := field.Type.(type) {
		case *ClassType:
			iResult, err := justifyClassType(ival, ftyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify class", datax.M{"name": fname})
			}
			result[fname] = iResult
		case *ArrayType:
			//mval, ok := ival.(datax.M)
			//if ok {
			//	if len(mval) > 0 {
			//		max := 0.0
			//		for k := range mval {
			//			max = math.Max(convertx.AnyToFloat64(k), max)
			//		}
			//		length := int(max) + 1
			//		arr := make([]interface{}, length, length)
			//		for k, val := range mval {
			//			arr[convertx.AnyToInt(k)] = val
			//		}
			//		ival = arr
			//	} else {
			//		ival = []interface{}{}
			//	}
			//}
			iResult, err := justifyArrayTypeToMap(ival, ftyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify array", datax.M{"name": fname})
			}
			result[fname] = iResult
		case *MapType:
			iResult, err := justifyMapType(ival, ftyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify map", datax.M{"name": fname})
			}
			result[fname] = iResult
		case *EnumType:
			iResult, err := JustifyEnumType(ival, ftyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify enum", datax.M{"name": fname})
			}
			result[fname] = iResult
		case *AnyType:
			result[fname] = ival
		case *BasicType:
			tagType := field.Tags["type"]
			switch tagType {
			default:
				iResult, err := justifyBasicType(ival, ftyp)
				if err != nil {
					return nil, errorx.Wrap(err, "justify basic", datax.M{"name": fname})
				}
				result[fname] = iResult
			}

		}

	}

	return result, nil
}

func justifyClassType(ival interface{}, ftyp *ClassType) (datax.M, error) {
	mval, ok := ival.(map[string]interface{})
	if ok == false {
		if ival == nil {
			return nil, nil
		}
		return nil, errorx.New("value not map[string]interface{}")
	}

	typDecl := GetDeclByKey(ftyp.Decl)
	if typDecl == nil {
		return nil, errorx.New("decl not found", datax.M{"class": ftyp.Decl})
	}

	return justifyObject(mval, typDecl.(*ClassDecl))
}
func justifyArrayTypeToMap(ival interface{}, ftyp *ArrayType) (datax.M, error) {
	if ival == nil {
		return nil, nil
	}

	var mval map[string]interface{}
	switch val := ival.(type) {
	case map[string]interface{}:
		mval = val
	case []interface{}:
		mval = map[string]interface{}{}
		for i, v := range val {
			mval[strconv.Itoa(i)] = v
		}
	default:
		if ival == nil {
			return nil, nil
		}
		return nil, errorx.New("value not map[string]interface{}")
	}
	newObj := datax.M{}
	for key, val := range mval {
		switch etyp := ftyp.Elem.(type) {
		case *ClassType:
			iResult, err := justifyClassType(val, etyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify map", datax.M{"key": key})
			}
			newObj[key] = iResult
		case *BasicType:
			iResult, err := justifyBasicType(val, etyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify basic", datax.M{"key": key})
			}
			newObj[key] = iResult
		case *EnumType:
			iResult, err := JustifyEnumType(val, etyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify map", datax.M{"key": key})
			}
			newObj[key] = iResult
		case *AnyType:
			iResult, err := JustifyAnyType(val, etyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify any", datax.M{"key": key})
			}
			newObj[key] = iResult
		default:
			return nil, errorx.New("not support type: " + etyp.String())

		}
	}

	return newObj, nil
}

func justifyArrayType(ival interface{}, ftyp *ArrayType) ([]interface{}, error) {
	if ival == nil {
		return nil, nil
	}
	arrValue, ok := ival.([]interface{})
	if ok == false {
		return nil, errorx.New("value not []interface{}")
	}

	newObj := make([]interface{}, len(arrValue))
	for index, ival := range arrValue {
		val := ival
		switch etyp := ftyp.Elem.(type) {
		case *ClassType:
			iResult, err := justifyClassType(val, etyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify map", datax.M{"index": index})
			}
			newObj[index] = iResult
		case *BasicType:
			iResult, err := justifyBasicType(val, etyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify basic", datax.M{"index": index})
			}
			newObj[index] = iResult
		case *EnumType:
			iResult, err := JustifyEnumType(val, etyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify map", datax.M{"index": index})
			}
			newObj[index] = iResult
		case *AnyType:
			iResult, err := JustifyAnyType(val, etyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify any", datax.M{"index": index})
			}
			newObj[index] = iResult
		default:
			return nil, errorx.New("not support type: " + etyp.String())
		}

	}

	return newObj, nil
}

func justifyMapType(ival interface{}, ftyp *MapType) (datax.M, error) {

	if ival == nil {
		return nil, nil
	}
	mval, ok := ival.(map[string]interface{})
	if ok == false {
		if ival == nil {
			return nil, nil
		}
		return nil, errorx.New("value not map[string]interface{}")
	}

	newObj := datax.M{}
	for key, val := range mval {
		switch etyp := ftyp.Value.(type) {
		case *ClassType:
			iResult, err := justifyClassType(val, etyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify map", datax.M{"key": key})
			}
			newObj[key] = iResult
		case *BasicType:
			iResult, err := justifyBasicType(val, etyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify basic", datax.M{"key": key})
			}
			newObj[key] = iResult
		case *EnumType:
			iResult, err := JustifyEnumType(val, etyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify map", datax.M{"key": key})
			}
			newObj[key] = iResult
		case *AnyType:
			iResult, err := JustifyAnyType(val, etyp)
			if err != nil {
				return nil, errorx.Wrap(err, "justify any", datax.M{"key": key})
			}
			newObj[key] = iResult
		default:
			return nil, errorx.New("not support type: " + etyp.String())

		}
	}

	return newObj, nil
}
func JustifyAnyType(ival interface{}, ftyp *AnyType) (interface{}, error) {
	if ival == nil {
		return ival, nil
	}
	mval, ok := ival.(datax.M)
	if !ok {
		return ival, nil
	}
	if mval == nil {
		return mval, nil
	}

	_type := mapx.String(mval, "_type")
	if _type == "" {
		return mval, nil
	}

	typDecl := GetDeclByKey(_type)
	if typDecl == nil {
		return nil, errorx.New("decl not found", datax.M{"_type": _type})
	}
	result, err := justifyObject(mval, typDecl.(*ClassDecl))
	result["_type"] = _type
	return result, err
}
func JustifyEnumType(ival interface{}, ftyp *EnumType) (interface{}, error) {
	typDecl := GetDeclByKey(ftyp.Decl)
	if typDecl == nil {
		return nil, errorx.New("decl not found", datax.M{"enum": ftyp.Decl})
	}
	enumDecl := typDecl.(*EnumDecl)

	return basickind.ConvertKindToStruct(basickind.Kind(enumDecl.Kind), ival), nil
}
func justifyBasicType(ival interface{}, ftyp *BasicType) (interface{}, error) {
	return basickind.ConvertKindToStruct(ftyp.Kind, ival), nil
}
